package cm.sms.impl;

import cm.exception.ServiceExceptionUtil;
import cm.sms.DO.SmsChannelDO;
import cm.sms.DO.SmsCodeDO;
import cm.sms.DO.SmsTemplateDO;
import cm.sms.Producer.SmsProducer;
import cm.sms.SmsClientFactory;
import cm.sms.SmsCommonResult;
import cm.sms.config.SmsCodeProperties;
import cm.sms.dto.SmsSendDTO;
import cm.sms.dto.SmsSendMessageDTO;
import cm.sms.dto.SmsSendRespDTO;
import cm.sms.inter.SmsClient;
import cm.sms.mapper.SmsCodeMapper;
import cm.sms.property.SmsChannelProperties;
import cm.sms.service.SmsChannelService;
import cm.sms.service.SmsLogService;
import cm.sms.service.SmsService;
import cm.sms.service.SmsTemplateService;
import cm.utils.KeyValueUtils;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.google.common.annotations.VisibleForTesting;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

import java.util.*;

import static cm.exception.ErrorCodeConstants.*;
import static cn.hutool.core.util.RandomUtil.randomInt;

/**
 * @author: yxh
 * @Description: 短信发送
 * @Date: Created in 2022/9/20 10:34
 * @Modified By:
 */
@Service
@Slf4j
public class SmsServiceImpl implements SmsService {
    @Resource
    private SmsCodeProperties smsCodeProperties;

    @Resource
    private SmsCodeMapper smsCodeMapper;

    @Resource
    private SmsTemplateService smsTemplateService;

    @Resource
    private SmsChannelService smsChannelService;

    @Resource
    private SmsLogService smsLogService;

    @Resource
    private SmsProducer smsProducer;

    @Resource
    private SmsClientFactory smsClientFactory;


    @Override
    public void sendSmsCode(SmsSendDTO smsSendDTO) {
        try {
            String code = createCode(smsSendDTO);
            sendSingleSms(smsSendDTO.getMobile(), null, null,
                    "test_02", MapUtil.of("code", code));
        }catch (Exception e){
            log.error(e.getMessage());
        }
    }
    public void sendSingleSms(String mobile, Long userId, Integer userType,
                              String templateCode, Map<String, Object> templateParams){
        try {
            //检验模板
//            SmsTemplateDO template = this.checkSmsTemplateValid(templateCode);
            SmsTemplateDO template = smsTemplateService.selectSmsTemplateData(templateCode);
            //检验渠道
//            SmsChannelDO smsChannelDO = this.checkSmsChannelValid(template.getChannelId());
            // 构建有序的模板参数。为什么放在这个位置，是提前保证模板参数的正确性，而不是到了插入发送日志
            List<KeyValueUtils<String, Object>> newTemplateParams = this.buildTemplateParams(template, templateParams);

            String content = StrUtil.format(template.getContent(), templateParams);
            Long sendLogId = smsLogService.createSmsLog(mobile, userId, userType, true, template, content, templateParams);

            // 发送 MQ 消息，异步执行发送短信
            smsProducer.sendSmsSendMessage(sendLogId, mobile, template.getChannelId(),
                    template.getApiTemplateId(), newTemplateParams);
        }catch (Exception e){
            log.error(e.getMessage());
        }
    }

    @VisibleForTesting
    public List<KeyValueUtils<String, Object>> buildTemplateParams(SmsTemplateDO template, Map<String, Object> templateParams) {
        List<KeyValueUtils<String, Object>> list = new ArrayList<>();
        for (String key:templateParams.keySet()){
            KeyValueUtils<String, Object> keyValueUtils = new KeyValueUtils<>();
            Object value = templateParams.get(key);
            if (Objects.isNull(value)) {
                throw ServiceExceptionUtil.exception(SMS_SEND_MOBILE_TEMPLATE_PARAM_MISS, key);
            }
            keyValueUtils.setValue(value);
            keyValueUtils.setKey(key);
            list.add(keyValueUtils);
        }
        return list;
//        return template.getParams().stream().map(key -> {
//            Object value = templateParams.get(key);
//            if (value == null) {
//                throw ServiceExceptionUtil.exception(SMS_SEND_MOBILE_TEMPLATE_PARAM_MISS, key);
//            }
//            return new KeyValueUtils(key, value);
//        }).collect(Collectors.toList());
    }


    @VisibleForTesting
    public SmsTemplateDO checkSmsTemplateValid(String templateCode) {
        // 获得短信模板。考虑到效率，从缓存中获取
        SmsTemplateDO template = smsTemplateService.getSmsTemplateByCodeFromCache(templateCode);
        // 短信模板不存在
        if (template == null) {
            throw ServiceExceptionUtil.exception(SMS_TEMPLATE_NOT_EXISTS);
        }
        return template;
    }

    @VisibleForTesting
    public SmsChannelDO checkSmsChannelValid(Long id) {
        // 获得短信模板。考虑到效率，从缓存中获取
        SmsChannelDO smsChannelDO = smsChannelService.getSmsChannel(id);
        // 短信模板不存在
        if (smsChannelDO == null) {
            throw ServiceExceptionUtil.exception(SMS_CHANNEL_NOT_EXISTS);
        }
        return smsChannelDO;
    }

    public String createCode(SmsSendDTO smsSendDTO) throws Exception{
        SmsCodeDO smsCodeDO = smsCodeMapper.selectLastCodeByQueryWrapper(smsSendDTO);
        if (ObjectUtil.isNotNull(smsCodeDO)){
            if (smsCodeDO.getTodayIndex() >= smsCodeProperties.getSendMaximumQuantityPerDay()) {
                // 超过当天发送的上限。
                throw new Exception("超过每日短信发送数量");
            }
            if (System.currentTimeMillis() - smsCodeDO.getCreateTime().getTime()
                    < smsCodeProperties.getSendFrequency().toMillis()) {
                throw new Exception("短信发送过于频率");

            }
        }
        // 创建验证码记录
        String code = String.valueOf(randomInt(smsCodeProperties.getBeginCode(), smsCodeProperties.getEndCode() + 1));
        SmsCodeDO newSmsCode = SmsCodeDO.builder()
                .mobile(smsSendDTO.getMobile())
                .code(code)
                .todayIndex(smsCodeDO != null ? smsCodeDO.getTodayIndex() + 1 : 1)
                .used(false).build();
        newSmsCode.setCreateTime(new Date());
        newSmsCode.setUpdateTime(new Date());
        smsCodeMapper.insert(newSmsCode);
        return code;
    }

    @Override
    public void doSendSms(SmsSendMessageDTO message) {
        // 获得渠道对应的 SmsClient 客户端
        SmsClient smsClient = smsClientFactory.getSmsClient(message.getChannelId());
        //补救措施，初始化数据为空
        if (Objects.isNull(smsClient)){
            SmsChannelProperties properties = new SmsChannelProperties();
            SmsChannelDO smsChannelDO = smsChannelService.getSmsChannel(message.getChannelId());
            log.info(JSON.toJSONString(smsChannelDO));
            if (Objects.nonNull(smsChannelDO)){
                BeanUtil.copyProperties(smsChannelDO,properties);
            }
            smsClient = smsClientFactory.createOrUpdateSmsClient(properties);
        }

        Assert.notNull(smsClient, "短信客户端({}) 不存在", message.getChannelId());
        // 发送短信
        if (Objects.nonNull(message)){
            SmsCommonResult<SmsSendRespDTO> sendResult = smsClient.sendSms(message.getLogId(), message.getMobile(),
                    message.getApiTemplateId(), message.getTemplateParams());
//            smsLogService.updateSmsSendResult(message.getLogId(), sendResult.getCode(), sendResult.getMsg(),
//                    sendResult.getApiCode(), sendResult.getApiMsg(), sendResult.getApiRequestId(),
//                    sendResult.getData() != null ? sendResult.getData().getSerialNo() : null);
        }
    }
}
